<?php

// 应用公共文件

// 获取文件夹下文件数量
function getFileNumber($path): int
{
    $handle = opendir($path);
    $i = 0;
    while(false !== $file=(readdir($handle))){
        if($file !== '.' && $file != '..')
        {
            $i++;
        }
    }
    closedir($handle);
    return $i;
}

// 手机版访问
function isMobile(): bool
{
    return request()->isMobile();
}

// 按照字符串个数切割字符串
function hcSubstr($str,$len,$start=0): string
{
    if (empty($str)) {
        return '';
    }
    //返回字符串中的前100字符串长度的字符
    return mb_substr($str, $start, $len, 'utf-8');
}

// sql切割
function hcSplitSql($file,$prefix,$charset = 'utf8mb4', $defaultTablePre = '$prefix$', $defaultCharset = 'utf8mb4')
{
    if (file_exists($file)) {
        //读取SQL文件
        $sql = file_get_contents($file);
        $sql = str_replace("\r", "\n", $sql);
        $sql = str_replace("BEGIN;\n", '', $sql);//兼容 navicat 导出的 insert 语句
        $sql = str_replace("COMMIT;\n", '', $sql);//兼容 navicat 导出的 insert 语句
        $sql = str_replace($defaultCharset, $charset, $sql);
        $sql = trim($sql);
        //替换表前缀
        $sql  = str_replace("{$defaultTablePre}", "{$prefix}", $sql);
        $sqls = explode(";\n", $sql);
        return $sqls;
    }

    return [];
}

function sp_testwrite($d)
{
    $tfile = "_test.txt";
    $fp    = @fopen($d . "/" . $tfile, "w");
    if (!$fp) {
        return false;
    }
    fclose($fp);
    $rs = @unlink($d . "/" . $tfile);
    if ($rs) {
        return true;
    }
    return false;
}

function sp_dir_create($path, $mode = 0777)
{
    if (is_dir($path))
        return true;
    $ftp_enable = 0;
    $path       = sp_dir_path($path);
    $temp       = explode('/', $path);
    $cur_dir    = '';
    $max        = count($temp) - 1;
    for ($i = 0; $i < $max; $i++) {
        $cur_dir .= $temp[$i] . '/';
        if (@is_dir($cur_dir))
            continue;
        @mkdir($cur_dir, 0777, true);
        @chmod($cur_dir, 0777);
    }
    return is_dir($path);
}

function sp_dir_path($path)
{
    $path = str_replace('\\', '/', $path);
    if (substr($path, -1) != '/')
        $path = $path . '/';
    return $path;
}

function sp_execute_sql($db, $sql)
{
    $sql = trim($sql);
    if (substr($sql, 0, 12) == 'CREATE TABLE') {
        $table_name = preg_replace("/^CREATE TABLE `(\w+)` .*/s", "\\1", $sql);;
        $msg        = "创建数据表 {$table_name} ";
        try {
            $db->execute($sql);
            return [
                'type' => 1, // 创建表
                'error'   => 0,
                'message' => $msg . '成功！'
            ];
        } catch (\Exception $e) {
            return [
                'type' => 1, // 创建表
                'error'     => 1,
                'message'   => $msg . '失败！',
                'exception' => $e->getMessage()
            ];
        }

    } else {
        try {
            $db->execute($sql);
            return [
                'type' => 2, // 执行sql语句
                'error'   => 0,
                'message' => 'SQL执行成功!'
            ];
        } catch (\Exception $e) {
            return [
                'type' => 2, // 执行sql语句
                'error'     => 1,
                'message'   => 'SQL执行失败！',
                'exception' => $e->getMessage()
            ];
        }
    }
}

function hcInstalled(): bool
{
    return file_exists(CMS_ROOT.'/data/install.lock');
}


/**
 * @throws \app\exception\ModelException
 * @throws \app\exception\ModelEmptyException
 */
function hcUrl($url, $vars, $suffix=true, $domain=false): \think\route\Url
{
    $url = strtolower($url);
    if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
        $info = parse_url($url);
        $url  = !empty($info['path']) ? $info['path'] : '';
        if (isset($info['fragment'])) {
            // 解析锚点
            $anchor = $info['fragment'];
            if (false !== strpos($anchor, '?')) {
                // 解析参数
                list($anchor, $info['query']) = explode('?', $anchor, 2);
            }
            if (false !== strpos($anchor, '@')) {
                // 解析域名
                list($anchor, $domain) = explode('@', $anchor, 2);
            }
        } elseif (strpos($url, '@') && false === strpos($url, '\\')) {
            // 解析域名
            list($url, $domain) = explode('@', $url, 2);
        }
    }
    if(0 == strpos($url, '/')){
        $url = ltrim($url,'/');
    }
    // 解析参数
    if (is_string($vars)) {
        // aaa=1&bbb=2 转换成数组
        parse_str($vars, $vars);
    }

    if (isset($info['query'])) {
        // 解析地址里面参数 合并到vars
        parse_str($info['query'], $params);
        $vars = array_merge($params, $vars);
    }

    if (!empty($anchor)) {
        $url = $url . '#' . $anchor;
    }
    if($url == 'list/index' && isset($vars['id'])){
        $url = $url . '?id=' . $vars['id'];
        unset($vars['id']);
    }
    if($url == 'detail/index' && isset($vars['cid'])){
        $url = $url . '?cid=' . $vars['cid'];
        unset($vars['cid']);
    }
    if($url == 'tag/index'){
        $url = $url . '?id=' . $vars['id'];
    }
    return url($url, $vars, $suffix, $domain);
}


function optEventLog($id,$title,$operation)
{
    $optAdmin = request()->admin;
    event("AdminOptLog",[
        'admin_id' => $optAdmin['uid'],
        'admin_name' => $optAdmin['name'],
        'title'=> $title . "管理",
        "content"=> $operation . $title . ','. $title . 'ID为'. $id,
    ]);
}

/**
 * 判断是否为windows系统
 * @return bool
 */
function windows_os(): bool
{
    return PHP_OS_FAMILY === 'Windows';
}

 function is_directory($directory): bool
 {
    return is_dir($directory);
}

// 获取文件后缀
function getFileExt($str): string
{
    return strtolower(pathinfo($str, PATHINFO_EXTENSION));
}
/**
 * 返回文件格式
 * @param string $str 文件名
 * @return string      文件格式
 */
function fileFormat(string $str): string
{
    // 取文件后缀名
    $str=getFileExt($str);
    // 图片格式
    $image=array('webp','jpg','png','ico','bmp','gif','tif','pcx','tga','bmp','pxc','tiff','jpeg','exif','fpx','svg','psd','cdr','pcd','dxf','ufo','eps','ai','hdri');
    // 视频格式
    $video=array('mp4','avi','3gp','rmvb','wmv','mkv','mpg','vob','mov','flv','swf','ape','wma','aac','mmf','amr','m4a','m4r','ogg','wav','wavpack','m4v');
    // 音频格式
    $audio=array('mp3','mpeg','wma','mid','cd','wave','aiff','mpeg-4','midi','flac','acc','ape','amr');
    // 压缩格式
    $zip=array('rar','zip','tar','cab','uue','jar','iso','z','7-zip','ace','lzh','arj','gzip','bz2','tz');
    // 文档格式
    $text=array('exe','doc','ppt','xls','wps','txt','lrc','wfs','torrent','html','htm','java','js','css','less','php','pdf','pps','host','box','docx','word','perfect','dot','dsf','efe','ini','json','lnk','log','msi','ost','pcs','tmp','xlsb');
    // 匹配不同的结果
    switch ($str) {
        case in_array($str, $image):
            return 'image';
            break;
        case in_array($str, $video):
            return 'video';
            break;
        case in_array($str, $zip):
            return 'zip';
            break;
        case in_array($str,$audio):
            return 'audio';
            break;
        default:
            return 'file';
            break;
    }
}

/**
 * 获取网站跟目录
 * @return string
 */
function hcGetRoot(): string
{
    $root = request()->root();
    $root = str_replace("//", '/', $root);
    $root = str_replace('/index.php', '', $root);
    if (defined('APP_NAMESPACE') && APP_NAMESPACE == 'api') {
        $root = preg_replace('/\/api$/', '', $root);
    }
    return rtrim($root, '/');
}

/**
 * 扫描路径下所有文件包括子目录
 * @param $dir
 * @return array
 */
function scanSubDir($dir): array
{
//    $dir     = ltrim($dir, "/");
    $dirs    = [];
    $subDirs = hcScanDir("$dir/*", GLOB_ONLYDIR);
    if (!empty($subDirs)) {
        foreach ($subDirs as $subDir) {
            $subDir = "$dir/$subDir";
            array_push($dirs, $subDir);
            $subDirSubDirs = scanSubDir($subDir);
            if (!empty($subDirSubDirs)) {
                $dirs = array_merge($dirs, $subDirSubDirs);
            }
        }
    }
    return $dirs;
}

/**
 * 判断文件是否存在
 * @param $filename
 * @return bool
 */
function hcFileExist($filename): bool
{
    if (is_file($filename)) {
        if (env('app_debug')) {
            if (basename(realpath($filename)) != basename($filename))
                return false;
        }
        return true;
    }
    return false;
}

/**
 * 扫描路径下的文件
 * @param $pattern
 * @param null $flags
 * @return array
 */
function hcScanDir($pattern,$flags = null): array
{
    $files = glob($pattern, $flags);
    if (empty($files)) {
        $files = [];
    } else {
        $files = array_map('basename', $files);
    }
    return $files;
}

/**
 * 小驼峰格式
 *
 * @param string $uncamelized_words
 * @param string $separator
 * @return string
 */
function camelize(string $uncamelized_words, string $separator='_'): string
{
    $uncamelized_words = $separator. str_replace($separator, " ", strtolower($uncamelized_words));
    return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator );
}

/**
 * 下划线格式
 *
 * @param string $camelCaps
 * @param string $separator
 * @return string
 */
function uncamelize(string $camelCaps, string $separator='_'): string
{
    return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
}

/**
 * 模型内统一数据返回
 * @param $code
 * @param string $msg
 * @param array $data
 * @return array
 */
function dataReturn($code, $msg = 'success', $data = []) {

    return ['code' => $code, 'data' => $data, 'msg' => $msg];
}

/**
 * 统一返回json数据
 * @param $code
 * @param string $msg
 * @param $data
 * @return \think\response\Json
 */
function jsonReturn($code, string $msg = 'success', $data = []): \think\response\Json
{

    return json(['code' => $code, 'data' => $data, 'msg' => $msg]);
}

/**
 * 生成用户密码
 * @param $password
 * @param string $salt
 * @return string
 */
function makePassword($password, string $salt = ''): string
{

    if (empty($salt)) {
        $salt = config('system.salt');
    }

    return sha1(md5($password . $salt));
}

/**
 * 校验密码
 * @param $inputPassword
 * @param $dbPassword
 * @param string $salt
 * @return bool
 */
function checkPassword($inputPassword, $dbPassword, string $salt = ''): bool
{

    if (makePassword($inputPassword, $salt) == $dbPassword) {
        return true;
    }

    return false;
}

/**
 * 解析token中简短的用户信息
 * @param $token
 * @return array
 */
function getUserSimpleInfo($token): array
{

    try {

        $token = (new \Lcobucci\JWT\Parser())->parse($token);
    }catch (\Exception $e) {

        return dataReturn(-1, $e->getMessage());
    }

    $data = new \Lcobucci\JWT\ValidationData();

    $data->setIssuer($token->getClaim('iss'));
    $data->setAudience($token->getClaim('aud'));
    $data->setId($token->getClaim('jti'));

    if(!$token->validate($data)) {
        return dataReturn(-2, 'token validate');
    }

    return dataReturn(0, '', [
        'uid' => $token->getClaim('uid'),
        'name' => $token->getClaim('name'),
        'seller_id' => $token->getClaim('seller_id'),
    ]);
}

/**
 * 从头部获取token
 * @return bool|string
 */
function getHeaderToken() {

    $header = request()->header();
    if(empty($header['authorization'])){
        $token = substr($header['Authorization'], 7);
    }else{
        $token = substr($header['authorization'], 7);
    }
    return $token;
}

/**
 * 统一分页返回
 * @param $list
 * @return array
 */
function pageReturn($list): array
{
    if (0 == $list['code']) {
        return ['code' => 0, 'msg' => 'ok', 'count' => $list['data']->total(), 'data' => $list['data']->all()];
    }

    return ['code' => 0, 'msg' => 'ok', 'count' => 0, 'data' => []];
}

// 引用实现无限分类
function generate(array $data,$flag = false): array
{
    $data = getColumnForKeyArray($data, 'id');
    $tree = [];
    foreach ($data as $k => $v) {
        $data[$k]['children'] = [];
        if (isset($data[$v['parent_id']])) {
            $data[$v['parent_id']]['children'][] = &$data[$k];
        } else {
            $tree[] = &$data[$k];
        }
    }
    return $tree;
}

// 数组值作为键
function getColumnForKeyArray(array $data, string $key): array
{
    $item = [];
    foreach ($data as $val) {
        if (! array_key_exists($key, $val)) {
            break;
        }
        $item[$val[$key]] = $val;
    }
    return $item;
}

// 自定义树形结构层数
function makeTreeWithMaxLevel($data,$pid=0,$maxLevel=0,$level=1): array
{
    $tree = array();
    foreach ($data as $key => $value) {
        if ($value['parent_id'] == $pid) {
            $value['level'] = $level;
            unset($data[$key]);
            if($maxLevel === 0 || $maxLevel > $level){
                $value['children'] = makeTreeWithMaxLevel($data, $value['id'],$maxLevel,$level+1);
            }else{
                $value['children'] = [];
            }
            $tree[] = $value;
        }
    }
    return $tree;
}

/**
 * // 递归实现无限分类
 *
 * @param $data
 * @param int $pid
 * @param int $level
 * @return array
 */
function makeTree($data, int $pid=0, int $level=0): array
{
    $tree = array();
    foreach ($data as $key => $value) {
        $value['children'] = [];
        if ($value['parent_id'] == $pid) {
            $value['level'] = $level;
            unset($data[$key]);
            $value['children'] = makeTree($data, $value['id'],$level+1);
            $tree[] = $value;
        }
    }
    return $tree;
}

/**
 * 获取某月所有时间
 * @param string $time 某天时间戳
 * @param string $format 转换的时间格式
 * @return array
 */
function getMonth($time = '', $format='Y-m-d')
{
    $time = $time != '' ? $time : time();
    // 获取当前周几
    $week = date('d', $time);
    $date = [];
    for ($i = 1; $i <= date('t', $time); $i++) {
        $date[date($format, strtotime('+' . ($i - $week) . ' days', $time))] = 0 ;
    }
    return $date;
}


/**
 * curl post数据
 * @param $url
 * @param $postData
 * @return array
 */
function curlPost($url, $postData) {

    // 初始化
    $curl = curl_init();
    // 设置抓取的url
    curl_setopt($curl, CURLOPT_URL, $url);
    // 设置头文件的信息作为数据流输出
    curl_setopt($curl, CURLOPT_HEADER, 0);
    // 设置获取的信息以文件流的形式返回，而不是直接输出。
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    // 超时设置
    curl_setopt($curl, CURLOPT_TIMEOUT, 10);
    // 超时设置，以毫秒为单位
    // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 500);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
    // 设置post方式提交
    curl_setopt($curl, CURLOPT_POST, 1);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
    // 执行命令
    $data = curl_exec($curl);
    curl_close($curl);

    return dataReturn(0, '成功', $data);
}

/**
 * curl get 请求
 * @param $url
 * @return array
 */
function curlGet($url)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_FAILONERROR, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_AUTOREFERER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    // $SSL = substr($url, 0, 8) == "https://" ? true : false;
    // if ($SSL) {
    //     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 信任任何证书
    //     curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 检查证书中是否设置域名
    // }
    $data = curl_exec($ch);
    curl_close($ch);
    return dataReturn(0, '成功', $data);
}

/**
 * 两个日期间全部的数据
 * @param $start
 * @param $end
 * @return array
 */
function getBetweenDate($start, $end) {

    $returnDate = [];
    $dtStart = strtotime($start);
    $dtEnd = strtotime($end);
    while ($dtStart <= $dtEnd) {
        $returnDate[] = date('m-d', $dtStart);
        $dtStart = strtotime('+1 day', $dtStart);
    }

    return $returnDate;
}


/**
 * 获取树形结构指定值子级
 * @param $tree
 * @param $name
 * @param $value
 * @return array
 */
function getTreeDataValue($tree,$name,$value){
    $resTree = array();
    foreach($tree as $item){
        if ($item[$name] == $value){
            $resTree[] = $item[$name];
            if (is_array($item) && isset($item['children'])){
                $resTree = array_merge($resTree,getTreeData($item['children'],$name));
            }
        }else{
            if (is_array($item) && isset($item['children'])){
                $resTree = array_merge($resTree,getTreeDataValue($item['children'],$name,$value));
            }
        }
    }
    return $resTree;
}


/**
 * 获取树形结构指定值
 * @param $tree
 * @param $name
 * @param $value
 * @return array
 */
function getTreeData($tree,$name){
    $resTree = array();
    foreach($tree as $item){
        $resTree[] = $item[$name];
        if (is_array($item) && isset($item['children'])){
            $resTree = array_merge($resTree,getTreeData($item['children'],$name));
        }
    }
    return $resTree;
}


/**
 * 生成下载表格
 * @param $fileName
 * @param $sheetName
 * @param $data
 * @param $matchedData
 * @return array
 */
function createDownloadExcel( $fileName, $sheetName, $head,$data, $matchedData )
{
    $config = [
        'path' => config('suwork.excel_url'),
    ];
    
    $fileName   = $fileName.date('Y_m_d_H_i').'.xlsx';
    $xlsxObject = new \Vtiful\Kernel\Excel($config);

    $formatData = [];
    foreach ($data as $key => $value) {
        foreach ($matchedData as $k => $val) {
            if (isset($value[$k])) {
                if (is_array($val)) {
                    if (isset($val[$value[$k]])) {
                        $formatData[$key][] = $val[$value[$k]];
                    }else{
                        $formatData[$key][] = '';
                    }
                }else{
                    $formatData[$key][] = $value[$k];
                }
            }else{
                $formatData[$key][] = '';
            }
        }
    }
    $formatData = array_values($formatData);
 
    $filePath = $xlsxObject->fileName($fileName, $sheetName)
        ->header($head)
        ->data($formatData)
        ->output();
    // Set Header
    header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    header('Content-Disposition: attachment;filename="' . $fileName . '"');
    header('Content-Length: ' . filesize($filePath));
    header('Content-Transfer-Encoding: binary');
    header('Cache-Control: must-revalidate');
    header('Cache-Control: max-age=0');
    header('Pragma: public');

    ob_clean();
    flush();

    if (copy($filePath, 'php://output') === false) {
        return jsonReturn(-1, '');
    }
    // Delete temporary file
    @unlink($filePath);
    die();
}

/**
 * 参数过滤
 * @param $class
 * @return array
 * @throws array
 */
function requestFilter($class) {

    try {

        $class = new \ReflectionClass($class);

        $properties = $class->getProperties();
        $propertiesMap = [];
        foreach ($properties as $vo) {
            $propertiesMap[] = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $vo->name));
        }

        return \think\facade\Request::only($propertiesMap);
    } catch (\ReflectionException $e) {

        return [];
    }
}

/**
 * 获取设备信息
 * @param $ua
 * @return array
 */
function getDeviceInfo($ua)
{
    $deviceOs = '未知设备';
    $deviceVersion = '未知版本';
    // $ua = $_SERVER['HTTP_USER_AGENT'];
    if (strpos($ua, 'Android') !== false) {

        preg_match("/(?<=Android )[\d\.]{1,}/", $ua, $version);
        $deviceOs = 'Android';
        $deviceVersion = $version[0];
    } elseif (strpos($ua, 'iPhone') !== false) {

        preg_match("/(?<=CPU iPhone OS )[\d\_]{1,}/", $ua, $version);
        $deviceOs = 'iPhone';
        $deviceVersion = str_replace('_', '.', $version[0]);
    } elseif (strpos($ua, 'iPad') !== false) {

        preg_match("/(?<=CPU OS )[\d\_]{1,}/", $ua, $version);
        $deviceOs = 'iPad';
        $deviceVersion = str_replace('_', '.', $version[0]);

    } elseif (preg_match('/OmniWeb\/(v*)([^\s|;]+)/i', $ua, $regs)) {

        $deviceOs  = 'OmniWeb';
        $deviceVersion   = $regs[2];
    }elseif (preg_match('/Netscape([\d]*)\/([^\s]+)/i', $ua, $regs)) {

        $deviceOs = 'Netscape';
        $deviceVersion = $regs[2];
    }elseif (preg_match('/safari\/([^\s]+)/i', $ua, $regs) && !preg_match('/Chrome\/([^\s]+)/i', $ua, $regs2)) {

        $deviceOs = 'Safari';
        $deviceVersion = $regs[1];
    }elseif (preg_match('/MSIE\s([^\s|;]+)/i', $ua, $regs)) {

        $deviceOs = 'Internet Explorer';
        $deviceVersion = $regs[1];
    }elseif (preg_match('/Opera[\s|\/]([^\s]+)/i', $ua, $regs)) {

        $deviceOs = 'Opera';
        $deviceVersion = $regs[1];
    }elseif (preg_match('/NetCaptor\s([^\s|;]+)/i', $ua, $regs)) {

        $deviceOs  = '(Internet Explorer) NetCaptor';
        $deviceVersion  = $regs[1];
    }elseif (preg_match('/Maxthon/i', $ua, $regs)) {

        $deviceOs = '(Internet Explorer) Maxthon';
        $deviceVersion = '';
    } elseif (preg_match('/360SE/i', $ua, $regs)) {

        $deviceOs = '(Internet Explorer) 360SE';
        $deviceVersion   = '';
    } elseif (preg_match('/SE 2.x/i', $ua, $regs)) {

        $deviceOs = '(Internet Explorer) 搜狗';
        $deviceVersion = '';
    }elseif (preg_match('/FireFox\/([^\s]+)/i', $ua, $regs)) {

        $deviceOs  = 'FireFox';
        $deviceVersion   = $regs[1];
    }elseif (preg_match('/Lynx\/([^\s]+)/i', $ua, $regs)) {

        $deviceOs  = 'Lynx';
        $deviceVersion   = $regs[1];
    }elseif(preg_match('/Chrome\/([^\s]+)/i', $ua, $regs)) {

        $deviceOs  = 'Chrome';
        $deviceVersion   = $regs[1];
    }elseif(strpos($ua, 'Postman') !== false) {
        $deviceOs  = 'Postman';
        $deviceVersion = '';
    }

    return [
        'deviceOs' => $deviceOs,
        'deviceVersion' => $deviceVersion
    ];
}


/**
 * 根据ip定位
 * @param $ip
 * @param $type
 * @return string | array
 * @throws Exception
 */
function getLocationByIp($ip, $type = 1)
{
    $ip2region = new \Ip2Region();
    $info = $ip2region->btreeSearch($ip);
    if(empty($info) || empty($info['region'])){
        return ['province' => '未知', 'city' => '未知'];
    }
    $info = explode('|', $info['region']);

    $address = '';
    foreach($info as $vo) {
        if('0' !== $vo) {
            $address .= $vo . '-';
        }
    }

    if (2 == $type) {
        if (empty(array_filter($info))) {
            return ['province' => '未知', 'city' => '未知'];
        }
        return ['province' => $info['2'], 'city' => $info['3']];
    }

    return rtrim($address, '-');
}

/**
 * 快速排序
 * @param $arr
 */
function quickSort (& $arr) {
    $length = count($arr);

    if ($length <= 1) {
        return;
    }

    $middle = $arr[0];

    $left = [];
    $right = [];

    for ($i = 1; $i < $length; $i++) {
        if ($middle['pivot']['sort'] < $arr[$i]['pivot']['sort']) {
            $right[] = $arr[$i];
        } else {
            $left[] = $arr[$i];
        }
    }

    quickSort($left);
    quickSort($right);

    $arr = array_merge($left, [$middle], $right);

//    return $arr;
}